﻿using ScottPlot;
using ScottPlot.Colormaps;
using ScottPlot.Plottables;
using SkiaSharp;
using System.Numerics;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.Log;
using UtilZ.Dotnet.Ex.Model;
using UtilZ.Dotnet.ScottPlotEx;
using UtilZ.Dotnet.WindowsDesktopEx.Winform;

namespace WinFormApp
{
    public partial class FWaterfallDemo : Form
    {

        private readonly LogControl logControl;
        public FWaterfallDemo()
        {
            InitializeComponent();

            this.logControl = new LogControl()
            {
                Dock = DockStyle.Fill,
            };
            panelLog.Controls.Add(this.logControl);
        }


        private void WaterfallDemo_Load(object sender, EventArgs e)
        {
            try
            {
                UtilZ.Dotnet.Ex.ZLog.ZLoger.OutputOption = UtilZ.Dotnet.Ex.ZLog.ZLogerOutputOptions.Loger;
                var redirectAppenderToUI = (RedirectAppender)Loger.GetAppenderByName(null, "RedirectAppender");
                if (redirectAppenderToUI == null)
                {
                    redirectAppenderToUI = new RedirectAppender(new RedirectAppendConfig());
                    Loger.GetLoger(null).AddAppender(redirectAppenderToUI);
                }
                redirectAppenderToUI.RedirectOuput += (sender, e) =>
                {
                    try
                    {
                        string str = string.Format("{0} {1}", e.Item.Time.ToString(LogConstant.DateTimeFormat), e.Item.Content);
                        this.logControl.AddLog(str, e.Item.Level);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                };


                DropdownBoxHelper.BindingEnumToComboBox<PlotMoveDirection>(comboBoxMoveDirection, PlotMoveDirection.Up);
                DropdownBoxHelper.BindingEnumToComboBox<PlotDockDirection>(comboBoxTimeDockDirection, PlotDockDirection.Left);

                plot.SKControl.MouseClick += SKControl_MouseClick;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void SKControl_MouseClick(object sender, MouseEventArgs e)
        {
            if (this._waterfallPlot != null)
            {
                var originValue = this._waterfallPlot.GetValue(plot.Plot.GetCoordinates(e.X, e.Y));
                Loger.Info($"originValue:{originValue}");
            }
        }


        private Crosshair _crosshair = null;
        private void AddCross()
        {
            if (this._crosshair != null)
            {
                return;
            }

            this._crosshair = plot.Plot.Add.Crosshair(0, 0);
            this._crosshair.TextColor = ScottPlot.Colors.Red;
            this._crosshair.TextBackgroundColor = ScottPlot.Colors.Green;

            this._crosshair.IsVisible = true;
            plot.SKControl.MouseEnter += (s, e) =>
            {
                if (this._crosshair != null)
                {
                    this._crosshair.IsVisible = true;
                    plot.Refresh();
                    //this._crosshair.VerticalLine.IsVisible = true;
                    //this._crosshair.HorizontalLine.IsVisible = true;
                }

                Loger.Info("MouseEnter");
            };
            plot.SKControl.MouseLeave += (s, e) =>
            {
                if (this._crosshair != null)
                {
                    this._crosshair.IsVisible = false;
                    plot.Refresh();
                    //this._crosshair.VerticalLine.IsVisible = false;
                    //this._crosshair.HorizontalLine.IsVisible = false;
                }
                Loger.Info("MouseLeave");
            };
            plot.MouseMove += (s, e) =>
            {
                var mousePixel = new ScottPlot.Pixel(e.X, e.Y);
                ScottPlot.Coordinates mouseCoordinates = plot.Plot.GetCoordinates(mousePixel);
                this.Text = $"X={mouseCoordinates.X:N3}, Y={mouseCoordinates.Y:N3}";
                this._crosshair.Position = mouseCoordinates;
                this._crosshair.VerticalLine.Text = $"{mouseCoordinates.X:N3}";
                this._crosshair.HorizontalLine.Text = $"{mouseCoordinates.Y:N3}";
                plot.Refresh();

                //if (this._crosshair != null)
                //{
                //    ScottPlot.Coordinates coordinates = plot.Plot.GetCoordinates(ScottPlot.Pixel.Zero, plot.Plot.Axes.Bottom, plot.Plot.Axes.Left);
                //    this._crosshair.X = coordinates.X;
                //    this._crosshair.Y = coordinates.Y;
                //}
                //Loger.Info("MouseMove");
            };

            //if (this._verticalLine != null)
            //{
            //    return;
            //}

            //this._verticalLine = plot.Plot.Add.VerticalLine(0, 2);
            //this._horizontalLine = plot.Plot.Add.HorizontalLine(0, 2);

            //plot.MouseEnter += (s, e) =>
            //{
            //    if (this._crosshair != null)
            //    {
            //        this._crosshair.IsVisible = true;
            //    }

            //    Loger.Info("MouseEnter");
            //};
            //plot.MouseLeave += (s, e) =>
            //{
            //    if (this._crosshair != null)
            //    {
            //        this._crosshair.IsVisible = false;
            //    }
            //    Loger.Info("MouseLeave");
            //};
            //plot.MouseMove += (s, e) =>
            //{
            //    ScottPlot.Coordinates coordinates = plot.Plot.GetCoordinates(ScottPlot.Pixel.Zero, plot.Plot.Axes.Bottom, plot.Plot.Axes.Left);
            //    SkiaSharp.SKPointI point = this._waterfallPlot.GetIndexes(coordinates);
            //    //this._verticalLine.X = coordinates.X;
            //    //this._horizontalLine.Y = coordinates.Y;
            //    this._verticalLine.X = point.X;
            //    this._horizontalLine.Y = point.Y;
            //    Loger.Info("MouseMove");
            //};

        }









        private readonly object _lock = new object();
        private WaterfallPlot _waterfallPlot = null;
        private GreatWaterfallPlot _scaleWaterfallPlot = null;
        private RectanglePlot _rectanglePlot = null;
        private TimePlot _timePlot = null;
        private bool _timePlotRunning = false;
        private ThreadEx _generateDataThread = null;
        private int _dataWidth = 1024;//fftCount
        private int _dataHeight = 600;
        private double _min = -10d;
        private double _max = 50d;

        private int _timeStep = 0;
        private int _moveRegStep = 0;

        unsafe private void btnTest_Click(object sender, EventArgs e)
        {
            try
            {
                if (this._generateDataThread == null)
                {
                    this._generateDataThread = new ThreadEx(this.GenerateDataThreadMethod, "数据生成线程");
                }

                if (this._generateDataThread.IsRuning)
                {
                    this._generateDataThread.Stop();
                    this._timePlotRunning = false;
                    //timer1.Stop();
                    return;
                }

                //if (this._rectanglePlot != null)
                //{
                //    plot.Plot.Remove(this._rectanglePlot);
                //    this._rectanglePlot = null;
                //}

                //if (this._waterfallPlot != null)
                //{
                //    plot.Plot.Remove(this._waterfallPlot);
                //    this._waterfallPlot = null;
                //}

                if (this._waterfallPlot == null)
                {
                    this._waterfallPlot = new WaterfallPlot();
                    //this._waterfallPlot.ManualRange = new ScottPlot.Range(-100,150);
                    plot.Plot.PlottableList.Insert(0, this._waterfallPlot);

                    //色系图
                    plot.Plot.Add.ColorBar(this._waterfallPlot);
                }


                PlotMoveDirection moveOrientation = DropdownBoxHelper.GetEnumFromComboBox<PlotMoveDirection>(comboBoxMoveDirection);


                //heatmap.SetData(new DataSpan<double>(this._dataPointer.DataPtr, this._dataPointer.Length, this._dataPointer.Length), fftCount);
                var options = new WaterfallPlotOptions()
                {
                    FlipHorizontally = checkBoxHorFlip.Checked,
                    FlipVertically = checkBoxVerFlip.Checked,
                    CacheOriginValue = true,
                };
                if (moveOrientation == PlotMoveDirection.Up || moveOrientation == PlotMoveDirection.Down)
                {
                    this._dataHeight = plot.Height - 43;
                    this._dataWidth = 1024;
                }
                else
                {
                    this._dataHeight = 1024;
                    this._dataWidth = plot.Width;
                }

                this._waterfallPlot.Set(this._dataWidth, this._dataHeight, this._min, this._max, new Turbo(), moveOrientation, options);

                if (this._timePlot == null)
                {
                    this._timePlot = new TimePlot();
                    plot.Plot.PlottableList.Add(this._timePlot);
                }
                var timePlotOptions = new TimePlotOptions()
                {
                    FontSize = 15,
                    Font = SKTypeface.FromFamilyName("Arial"),
                    IsAntialias = true,
                    StepLimit = 100,
                    EdgeDistance = 10,
                    GetStepFunc = this.GetStepFunc2,
                };
                PlotDockDirection dockDirection = DropdownBoxHelper.GetEnumFromComboBox<PlotDockDirection>(comboBoxTimeDockDirection);
                this._timePlot.Set(this._dataWidth, this._dataHeight, moveOrientation, dockDirection, timePlotOptions);


                if (this._rectanglePlot == null)
                {
                    this._rectanglePlot = new RectanglePlot();
                }
                var rectanglePlotOptions = new RectanglePlotOptions()
                {
                    Enable = true,
                    StrokeColor = SKColors.Green,
                    StrokeWidth = 3.0f,
                };
                this._rectanglePlot.Set(this._dataWidth, this._dataHeight, moveOrientation, rectanglePlotOptions, this.GetMoveRegStepFunc);
                plot.Plot.PlottableList.Add(this._rectanglePlot);

                this._rectanglePlot.AddMarker(new RectangleMarker(10, 20, 100, 60));
                this._rectanglePlot.AddMarker(new RectangleMarker(500, 200, 100, 80));

                plot.Plot.Axes.Margins();
                plot.Plot.Axes.AutoScale();
                plot.Plot.Font.Automatic();//多种语言显示文本自动适配,否则可能会出现黑框,而不是文本
                                           //plot.Plot.Axes.SetLimits(100, 260, 0, 400);


                // tell an axis to use the custom tick generator
                plot.Plot.Axes.Bottom.TickGenerator = new ScottPlot.TickGenerators.NumericAutomatic()
                {
                    LabelFormatter = CustomBottomFormatter
                };
                plot.Plot.Axes.Bottom.Label.Text = "Bottom";
                plot.Plot.Axes.Left.Label.Text = "Left";
                plot.Plot.Axes.Top.Label.Text = "TopTopTopTopTopTop";

                //plot.Plot.Axes.Right.Label.Text = "Right";
                ////plot.Plot.Axes.Right.Label.OffsetX = 10;
                //plot.Plot.Axes.Right.Min = 10;
                //plot.Plot.Axes.Right.Max = 1000;
                //plot.Plot.Axes.Right.TickGenerator = new ScottPlot.TickGenerators.NumericAutomatic()
                //{
                //    LabelFormatter = CustomRightFormatter
                //};


                this.AddCross();

                //var yAxis2 = plot.Plot.Axes.AddRightAxis();
                //yAxis2.Label.Text = "AddRightAxis";
                //yAxis2.Label.OffsetX = 10;
                //yAxis2.LabelFontName = "AddRightAxis";


                //无效代码,估计已丢弃
                //string chinese = "中间标题";
                //plot.Plot.Axes.Title.Label.Text = chinese;
                //plot.Plot.Axes.Title.Label.FontName = ScottPlot.Fonts.Detect(chinese);

                var myPlot = plot.Plot;
                myPlot.Title("标题"); // Chinese
                                    //myPlot.YLabel("試験"); // Japanese
                                    //myPlot.XLabel("테스트"); // Korean
                myPlot.Font.Automatic();

                this._timePlotRunning = true;
                this._generateDataThread.Start();

                //timer1.Interval = (int)numRefreshIntervalMs.Value;
                //timer1.Start();
            }
            catch (Exception ex)
            {
                Loger.Error(ex);
            }
        }

        private int GetStepFunc2()
        {
            if (this._timePlotRunning)
            {
                this._moveRegStep = Interlocked.Exchange(ref this._timeStep, 0);
                return this._moveRegStep;
            }
            else
            {
                return -1;
            }
        }

        private int GetMoveRegStepFunc()
        {
            if (this._timePlotRunning)
            {
                return Interlocked.Exchange(ref this._moveRegStep, 0);
            }
            else
            {
                return -1;
            }
        }

        private long _beginFreqMHz = 100;
        //private long _endFreqMHz = 260;
        private long _duMhz = 160;
        private string CustomBottomFormatter(double position)
        {
            if (this._waterfallPlot == null)
            {
                return position.ToString();
            }

            if (this._waterfallPlot.MoveOrientation == PlotMoveDirection.Up ||
                this._waterfallPlot.MoveOrientation == PlotMoveDirection.Down)
            {
                return $"{Math.Round(this._beginFreqMHz + position * _duMhz / this._dataWidth, 3)}M";
            }
            else
            {
                if (position == 0)
                    return "0";
                else if (position > 0)
                    return $"+{position}";
                else
                    return $"({-position})";
            }
        }

        private string CustomRightFormatter(double position)
        {
            return $"{Math.Round(_duMhz * position / 1000, 3)}M";
        }


        //private string filePath = @"D:\sigALL\16QAM\50K\20240617181640_IQ_AAAA041108ZD01_F_0500000000_B_0000100000_S_0000128000_ns_498811946.wav";
        //private string filePath = @"E:\样本数据\跳频\IQ_dataVoice_25.6M.dat";
        private string filePath = @"E:\样本数据\BPSK-43EBN0-1200kbd-6400KHzFs.dat";
        unsafe private void GenerateDataThreadMethod(ThreadPara threadPara)
        {
            Loger.Info("已开始数据生成线程");
            try
            {
                int fftCount = this._dataWidth;


                //filePath = @"E:\Soft\OS\Windows2019签名机制补丁.iso";
                using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);

                long maxLength = fftCount * 10000;
                using var srcDataPointer = new UnmanagedMemoryPointer<byte>(fileStream.Length < maxLength ? filePath.Length : maxLength);
                fileStream.Read(srcDataPointer.GetSpan());


                var iqCount = srcDataPointer.LongLength / 4;
                var rowCount = (int)(iqCount / fftCount);
                Complex[] fftData = new Complex[fftCount];
                int readCount = 0;
                long perRowByteCount = fftCount * 4;
                double iData, qData = 0;

                int haf = fftCount / 2;
                int flagIndex = fftCount / 8;
                int flagEndIndex = flagIndex + 30;

                using var dstDataPointer = new UnmanagedMemoryPointer<double>(fftCount);
                Span<double> specData = dstDataPointer.GetSpan();

                Action rendrAction = new Action(() =>
                {
                    try
                    {
                        plot.Refresh();
                    }
                    catch (Exception)
                    { }
                });


                double temp;
                const double MIN = 0.000001d;
                double offset = 0d;
                while (!threadPara.Token.IsCancellationRequested)
                {
                    if (readCount >= rowCount)
                    {
                        readCount = 0;
                    }

                    short* ptr = (short*)(srcDataPointer.DataPtr + readCount * perRowByteCount);
                    for (int j = 0; j < fftCount; j++)
                    {
                        iData = *ptr++;
                        qData = *ptr++;
                        fftData[j] = new Complex(iData, qData);
                    }
                    MathNet.Numerics.IntegralTransforms.Fourier.Forward(fftData);

                    int iqIndex = haf;
                    for (int j = 0; j < haf; j++)
                    {
                        temp = Math.Sqrt(Math.Pow(fftData[iqIndex].Real, 2) + Math.Pow(fftData[iqIndex].Imaginary, 2));
                        if (temp < MIN)
                        {
                            temp = MIN;
                        }
                        temp = 10 * Math.Log10(temp);
                        specData[j] = temp;
                        iqIndex++;
                    }

                    iqIndex = 0;
                    for (int j = haf; j < fftCount; j++)
                    {
                        temp = Math.Sqrt(Math.Pow(fftData[iqIndex].Real, 2) + Math.Pow(fftData[iqIndex].Imaginary, 2));
                        if (temp < MIN)
                        {
                            temp = MIN;
                        }
                        temp = 10 * Math.Log10(temp);

                        specData[j] = temp;
                        iqIndex++;
                    }

                    //创建一个区分标识数据区
                    if (offset > 30)
                    {
                        offset = 0;
                    }
                    for (int j = flagIndex; j < flagEndIndex; j++)
                    {
                        specData[j] = specData[haf + j] + offset;
                    }
                    offset += 0.1d;

                    lock (this._lock)
                    {
                        this._waterfallPlot.AppendData(specData);
                        Interlocked.Increment(ref this._timeStep);
                    }
                    this.Invoke(rendrAction);

                    readCount++;

                    threadPara.WaitOne(20);
                }
            }
            catch (Exception ex)
            {
                Loger.Error(ex);
            }
            Loger.Info("已停止数据生成线程");
        }


        private int _lastDataWdith = 0;
        private int _lastDataHeight = 0;
        private UnmanagedMemoryPointer<double> _dataAllPointer = new UnmanagedMemoryPointer<double>(0);
        unsafe private void btntestAll_Click(object sender, EventArgs e)
        {
            PlotMoveDirection moveOrientation = DropdownBoxHelper.GetEnumFromComboBox<PlotMoveDirection>(comboBoxMoveDirection);
            var options = new WaterfallPlotOptions()
            {
                FlipHorizontally = checkBoxHorFlip.Checked,
                FlipVertically = checkBoxVerFlip.Checked,
                CacheOriginValue = true,
            };

            if (this._timePlot != null)
            {
                plot.Plot.Remove(this._timePlot);
                this._timePlot = null;
            }

            //if (this._waterfallPlot != null)
            //{
            //    plot.Plot.Remove(this._waterfallPlot);
            //    this._waterfallPlot = null;
            //    plot.Refresh();
            //}

            if (this._waterfallPlot == null)
            {
                this._waterfallPlot = new WaterfallPlot();
                plot.Plot.PlottableList.Add(this._waterfallPlot);
            }

            //var heatmap = plot.Plot.Add.Heatmap(data);
            if (moveOrientation == PlotMoveDirection.Up || moveOrientation == PlotMoveDirection.Down)
            {
                this._dataHeight = plot.Height - 43;
                this._dataWidth = 1024;
                this._waterfallPlot.Set(this._dataWidth, this._dataHeight, this._min, this._max, new Turbo(), moveOrientation, options);
            }
            else
            {
                this._dataHeight = 1024;
                this._dataWidth = plot.Width;
                this._waterfallPlot.Set(this._dataWidth, this._dataHeight, this._min, this._max, new Turbo(), moveOrientation, options);
            }




            if (this._dataAllPointer.Length == 0 || this._lastDataWdith != this._dataWidth || this._lastDataHeight != this._dataHeight)
            {
                using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
                using var pointer = new UnmanagedMemoryPointer<byte>(fileStream.Length);
                fileStream.Read(pointer.GetSpan());

                int fftCount = this._dataWidth;
                var iqcount = fileStream.Length / 4;
                var rowCount = (int)(iqcount / fftCount);
                rowCount /= 2;


                this._dataAllPointer.CheckSetLength(rowCount * fftCount);
                double[,] data = new double[rowCount, fftCount];
                int haf = fftCount / 2;
                int flagIndex = fftCount / 8;
                int flagEndIndex = flagIndex + 30;

                ParallelEx.ParallelExcuteNoMod<object>(null, 0, rowCount, 4, null, (item) =>
                {
                    double iData, qData = 0, temp;
                    Complex[] fftData = new Complex[fftCount];
                    short* ptr = (short*)pointer.DataPtr + item.BeginIndex * fftCount * 2;
                    double offset = 0d;

                    for (int i = item.BeginIndex; i < item.EndIndex; i++)
                    {
                        for (int j = 0; j < fftCount; j++)
                        {
                            iData = *ptr++;
                            qData = *ptr++;
                            fftData[j] = new Complex(iData, qData);
                        }
                        MathNet.Numerics.IntegralTransforms.Fourier.Forward(fftData);
                        Span<double> dataSpan = this._dataAllPointer.GetSpan().Slice(i * fftCount);

                        int iqIndex = haf;
                        for (int j = 0; j < haf; j++)
                        {
                            temp = 10 * Math.Log10(Math.Sqrt(Math.Pow(fftData[iqIndex].Real, 2) + Math.Pow(fftData[iqIndex].Imaginary, 2)));
                            data[i, j] = temp;
                            dataSpan[j] = temp;
                            iqIndex++;
                        }

                        iqIndex = 0;
                        for (int j = haf; j < fftCount; j++)
                        {
                            temp = 10 * Math.Log10(Math.Sqrt(Math.Pow(fftData[iqIndex].Real, 2) + Math.Pow(fftData[iqIndex].Imaginary, 2)));
                            data[i, j] = temp;
                            dataSpan[j] = temp;
                            iqIndex++;
                        }

                        //创建一个区分标识数据区
                        if (offset > 30)
                        {
                            offset = 0d;
                        }
                        for (int j = flagIndex; j < flagEndIndex; j++)
                        {
                            dataSpan[j] = dataSpan[haf + j]+ offset;
                        }
                        offset += 0.1d;
                    }
                });

                this._lastDataWdith = this._dataWidth;
                this._lastDataHeight = this._dataHeight;
            }

            Span<double> spacData = this._dataAllPointer.GetSpan();
            this._waterfallPlot.AppendDatas(spacData);


            if (this._rectanglePlot != null)
            {
                plot.Plot.Remove(this._rectanglePlot);
                this._rectanglePlot = null;
            }
            this._rectanglePlot = new RectanglePlot();
            var rectanglePlotOptions = new RectanglePlotOptions()
            {
                Enable = true,
                StrokeColor = SKColors.Blue,
                StrokeWidth = 3.0f,
            };
            this._rectanglePlot.Set(this._dataWidth, this._dataHeight, PlotMoveDirection.None, rectanglePlotOptions, null);
            plot.Plot.PlottableList.Add(this._rectanglePlot);
            this._rectanglePlot.ClearMarker();
            this._rectanglePlot.AddMarker(new RectangleMarker(10, 20, 100, 60));
            this._rectanglePlot.AddMarker(new RectangleMarker(500, 200, 100, 80));




            //int offset = 0;
            //for (int i = 0; i < rowCount; i++)
            //{
            //    heatmap.AppendData(spacData.Slice(offset, fftCount));
            //    offset += fftCount;
            //}



            plot.Plot.Axes.Margins();
            plot.Plot.Axes.AutoScale();
            plot.Refresh();
        }

        unsafe private void btnTestGreatWaterPlot_Click(object sender, EventArgs e)
        {
            PlotMoveDirection moveOrientation = DropdownBoxHelper.GetEnumFromComboBox<PlotMoveDirection>(comboBoxMoveDirection);
            if (this._timePlot != null)
            {
                plot.Plot.Remove(this._timePlot);
                this._timePlot = null;
            }

            if (this._waterfallPlot != null)
            {
                plot.Plot.Remove(this._waterfallPlot);
                this._waterfallPlot = null;
            }

            if (this._scaleWaterfallPlot == null)
            {
                this._scaleWaterfallPlot = new GreatWaterfallPlot();
                plot.Plot.PlottableList.Add(this._scaleWaterfallPlot);
            }

            //var heatmap = plot.Plot.Add.Heatmap(data);
            //if (moveOrientation == PlotMoveDirection.Up || moveOrientation == PlotMoveDirection.Down)
            //{
            //    this._dataHeight = plot.Height - 43;
            //    this._dataWidth = 1024;
            //}
            //else
            //{
            //    this._dataHeight = 1024;
            //    this._dataWidth = plot.Width;
            //}

            if (moveOrientation == PlotMoveDirection.Up || moveOrientation == PlotMoveDirection.Down)
            {
                this._dataHeight = 1250000;
                this._dataWidth = 8192;

                //this._dataHeight = 100000;
                //this._dataWidth = 1027;
            }
            else
            {
                this._dataHeight = 1024;
                this._dataWidth = plot.Width;
            }



            if (this._dataAllPointer.Length == 0 || this._lastDataWdith != this._dataWidth || this._lastDataHeight != this._dataHeight)
            {
                filePath = @"E:\Soft\SW_DVD5_Office_Professional_Plus_2013_64Bit_ChnSimp_MLF_X18-55285.ISO";
                using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
                var len = Math.Min((long)this._dataHeight * this._dataWidth * 4, fileStream.Length);
                using var pointer = new UnmanagedMemoryPointer<byte>(len);
                fileStream.Read(pointer.GetSpan());

                int fftCount = this._dataWidth;
                var iqcount = len / 4;
                var rowCount = (int)(iqcount / fftCount);


                this._dataAllPointer.CheckSetLength(rowCount * fftCount);
                int haf = fftCount / 2;
                int flagIndex = fftCount / 8;
                int flagEndIndex = flagIndex + 30;

                ParallelEx.ParallelExcuteNoMod<object>(null, 0, rowCount, 4, null, (item) =>
                {
                    double iData, qData = 0, temp;
                    Complex[] fftData = new Complex[fftCount];
                    short* ptr = (short*)pointer.DataPtr + item.BeginIndex * fftCount * 2;
                    double offset = 0d;

                    for (int i = item.BeginIndex; i < item.EndIndex; i++)
                    {
                        for (int j = 0; j < fftCount; j++)
                        {
                            iData = *ptr++;
                            qData = *ptr++;
                            fftData[j] = new Complex(iData, qData);
                        }
                        MathNet.Numerics.IntegralTransforms.Fourier.Forward(fftData);
                        Span<double> dataSpan = this._dataAllPointer.GetSpan().Slice(i * fftCount);

                        int iqIndex = haf;
                        for (int j = 0; j < haf; j++)
                        {
                            temp = 10 * Math.Log10(Math.Sqrt(Math.Pow(fftData[iqIndex].Real, 2) + Math.Pow(fftData[iqIndex].Imaginary, 2)));
                            dataSpan[j] = temp;
                            iqIndex++;
                        }


                        iqIndex = 0;
                        for (int j = haf; j < fftCount; j++)
                        {
                            temp = 10 * Math.Log10(Math.Sqrt(Math.Pow(fftData[iqIndex].Real, 2) + Math.Pow(fftData[iqIndex].Imaginary, 2)));
                            dataSpan[j] = temp;
                            iqIndex++;
                        }

                        //创建一个区分标识数据区
                        if (offset > 30)
                        {
                            offset = 0d;
                        }
                        for (int j = flagIndex; j < flagEndIndex; j++)
                        {
                            dataSpan[j] = dataSpan[haf + j] + offset;
                        }
                        offset += 0.1d;
                    }
                });

                this._lastDataWdith = this._dataWidth;
                this._lastDataHeight = this._dataHeight;
            }

            SpanZ<double> spacData = this._dataAllPointer.GetSpanZ();
            var options = new GreatWaterfallPlotOptions()
            {
                FlipHorizontally = checkBoxHorFlip.Checked,
                FlipVertically = checkBoxVerFlip.Checked,
                CacheOriginValue = true,
                DataWidth = this._dataWidth,
                DataHeight = this._dataHeight,
                PlotHeight = plot.Height - 43,
                PlotWidth = plot.Width,
                Min = this._min,
                Max = this._max,
                Direction = moveOrientation
            };
            this._scaleWaterfallPlot.Set(spacData, options);


            //if (this._rectanglePlot != null)
            //{
            //    plot.Plot.Remove(this._rectanglePlot);
            //    this._rectanglePlot = null;
            //}
            //this._rectanglePlot = new RectanglePlot();
            //var rectanglePlotOptions = new RectanglePlotOptions()
            //{
            //    Enable = true,
            //    StrokeColor = SKColors.Blue,
            //    StrokeWidth = 3.0f,
            //};
            //this._rectanglePlot.Set(this._dataWidth, this._dataHeight, PlotMoveDirection.None, rectanglePlotOptions, null);
            //plot.Plot.PlottableList.Add(this._rectanglePlot);
            //this._rectanglePlot.ClearMarker();
            //this._rectanglePlot.AddMarker(new RectangleMarker(10, 20, 100, 60));
            //this._rectanglePlot.AddMarker(new RectangleMarker(500, 200, 100, 80));




            //int offset = 0;
            //for (int i = 0; i < rowCount; i++)
            //{
            //    heatmap.AppendData(spacData.Slice(offset, fftCount));
            //    offset += fftCount;
            //}



            plot.Plot.Axes.Margins();
            plot.Plot.Axes.AutoScale();
            plot.Refresh();
        }



        unsafe private void btnMarker_Click(object sender, EventArgs e)
        {
            //int[,] testData = new int[2, 3];
            //int value = 1;
            //for (int i = 0; i < testData.GetLength(0); i++)
            //{
            //    for (int j = 0; j < testData.GetLength(1); j++)
            //    {
            //        testData[i, j] = value++;
            //    }
            //}

            //fixed (int* ptr = testData)
            //{
            //    Span<int> span = new Span<int>(ptr, 6);
            //}


            //plot.Plot.PlottableList.Clear();

            //this._dataHeight = 500;
            //this._dataWidth = 1024;
            ////var rectMarkerPlot = new RectMarkerPlot();            
            ////rectMarkerPlot.Set(this._dataWidth, this._dataHeight, 2f, SKColors.Green, RectMarkerPlotOptions.Default);
            ////rectMarkerPlot.AddMarker(new RectangleMarker(10, 20, 100, 60));
            ////rectMarkerPlot.AddMarker(new RectangleMarker(500, 200, 100, 80));


            //this._waterfallPlot = new WaterfallPlot();
            //if (moveOrientation == WaterfallPlotMoveOrientation.Up || moveOrientation == WaterfallPlotMoveOrientation.Down)
            //{
            //    this._dataHeight = plot.Height - 43;
            //    this._dataWidth = 1024;
            //    this._waterfallPlot.Set(this._dataWidth, this._dataHeight, this._min, this._max, new Turbo(), moveOrientation, options);
            //}
            //else
            //{
            //    this._dataHeight = 1024;
            //    this._dataWidth = plot.Width;
            //    this._waterfallPlot.Set(this._dataWidth, this._dataHeight, this._min, this._max, new Turbo(), moveOrientation, options);
            //}

            //var rectMarkerPlot = new WaterfallPlot();
            //rectMarkerPlot.Set(this._dataWidth, this._dataHeight, 2f, SKColors.Green, RectMarkerPlotOptions.Default);
            //rectMarkerPlot.AddMarker(new RectangleMarker(10, 20, 100, 60));
            //rectMarkerPlot.AddMarker(new RectangleMarker(500, 200, 100, 80));
            //plot.Plot.PlottableList.Add(rectMarkerPlot);

            //plot.Plot.Axes.Margins();
            //plot.Plot.Axes.AutoScale();
            //plot.Refresh();


            PlotMoveDirection moveOrientation = DropdownBoxHelper.GetEnumFromComboBox<PlotMoveDirection>(comboBoxMoveDirection);
            if (moveOrientation == PlotMoveDirection.Up || moveOrientation == PlotMoveDirection.Down)
            {
                this._dataHeight = plot.Height - 43;
                this._dataWidth = 1024;
            }
            else
            {
                this._dataHeight = 1024;
                this._dataWidth = plot.Width;
            }

            if (this._rectanglePlot != null)
            {
                plot.Plot.Remove(this._rectanglePlot);
                this._rectanglePlot = null;
            }
            this._rectanglePlot = new RectanglePlot();
            var rectanglePlotOptions = new RectanglePlotOptions()
            {
                Enable = true,
                StrokeColor = SKColors.Blue,
            };
            this._rectanglePlot.Set(this._dataWidth, this._dataHeight, PlotMoveDirection.None, rectanglePlotOptions, null);
            plot.Plot.PlottableList.Add(this._rectanglePlot);
            this._rectanglePlot.ClearMarker();
            this._rectanglePlot.AddMarker(new RectangleMarker(10, 20, 100, 60));
            this._rectanglePlot.AddMarker(new RectangleMarker(500, 200, 100, 80));

            plot.Plot.Axes.Margins();
            plot.Plot.Axes.AutoScale();
            plot.Refresh();
        }



        private void timer1_Tick(object sender, EventArgs e)
        {
            lock (this._lock)
            {
                plot.Refresh();
            }
        }


        private void btnTime_Click(object sender, EventArgs e)
        {
            try
            {
                if (this._timePlotRunning)
                {
                    timer1.Stop();
                    this._timePlotRunning = false;
                }
                else
                {
                    PlotMoveDirection moveOrientation = DropdownBoxHelper.GetEnumFromComboBox<PlotMoveDirection>(comboBoxMoveDirection);
                    if (moveOrientation == PlotMoveDirection.Up || moveOrientation == PlotMoveDirection.Down)
                    {
                        this._dataHeight = plot.Height - 43;
                        this._dataWidth = 1024;
                    }
                    else
                    {
                        this._dataHeight = 1024;
                        this._dataWidth = plot.Width;
                    }

                    this._dataHeight = plot.Height;
                    this._dataWidth = plot.Width;

                    if (this._rectanglePlot != null)
                    {
                        plot.Plot.Remove(this._rectanglePlot);
                        this._rectanglePlot = null;
                    }

                    if (this._timePlot == null)
                    {
                        this._timePlot = new TimePlot();
                        plot.Plot.PlottableList.Add(this._timePlot);
                    }
                    var timePlotOptions = new TimePlotOptions()
                    {
                        FontSize = 20,
                        Font = SKTypeface.FromFamilyName("Arial"),
                        IsAntialias = true,
                        StepLimit = 100,
                        EdgeDistance = 0,
                        GetStepFunc = this.GetStepFunc,
                    };

                    PlotDockDirection dockDirection = DropdownBoxHelper.GetEnumFromComboBox<PlotDockDirection>(comboBoxTimeDockDirection);
                    this._timePlot.Set(this._dataWidth, this._dataHeight, moveOrientation, dockDirection, timePlotOptions);

                    plot.Plot.Axes.Margins();
                    plot.Plot.Axes.AutoScale();
                    plot.Refresh();

                    timer1.Interval = 1000;
                    timer1.Start();
                    this._timePlotRunning = true;
                }
            }
            catch (Exception ex)
            {
                Loger.Error(ex);
            }
        }

        private int GetStepFunc()
        {
            if (this._timePlotRunning)
            {
                //return 20;
                return 100;
            }
            else
            {
                return -1;
            }
        }

        private void btnTestDraw_Click(object sender, EventArgs e)
        {
            try
            {
                using var sourceBitmap = SKBitmap.Decode(@"D:\Temp\demo.png");
                //SaveImg(@"D:\Temp\demo11.png", sourceBitmap);

                using var paint = new SKPaint()
                {
                    FilterQuality = SKFilterQuality.High
                    //FilterQuality =  SKFilterQuality.None
                };
                int offset = 500;
                int targetHeight = sourceBitmap.Height - offset;
                using var destBitmap = new SKBitmap(sourceBitmap.Width, targetHeight, sourceBitmap.Info.ColorType, sourceBitmap.Info.AlphaType);

                SKRect source = new SKRect(0, offset, sourceBitmap.Width, sourceBitmap.Height);
                SKRect dest = new SKRect(0, 0, sourceBitmap.Width, destBitmap.Height);
                using var destBitmapCanvas = new SKCanvas(destBitmap);
                destBitmapCanvas.DrawBitmap(sourceBitmap, source, dest, paint);
                //destBitmapCanvas.DrawBitmap(sourceBitmap, new SKRect(0, offset, 0, targetHeight), paint);
                //destBitmapCanvas.Clear(SKColors.Red);
                destBitmapCanvas.Flush();

                SaveImg(@"D:\Temp\demo2.png", destBitmap);
                Loger.Info("ok");
            }
            catch (Exception ex)
            {
                Loger.Error(ex);
            }
        }

        private void SaveImg(string filePath, SKBitmap bitmap)
        {
            FileEx.TryDeleFile(filePath);
            using var fileStream = new FileStream(filePath, FileMode.CreateNew, FileAccess.Write);
            SKData data = bitmap.Encode(SKEncodedImageFormat.Png, 100);
            data.SaveTo(fileStream);
            //using var tempImg = SKImage.FromBitmap(destBitmap);
            //SKData data = tempImg.Encode(SKEncodedImageFormat.Png, 100);
            //data.SaveTo(fileStream);
        }

    }
}
